home *** CD-ROM | disk | FTP | other *** search
/ Aminet 45 / Aminet 45 (2001)(GTI - Schatztruhe)[!][Oct 2001].iso / Aminet / game / role / ldmud-3.2-bin.lha / mud / doc / LPC / mappings < prev    next >
Text File  |  2001-04-06  |  18KB  |  432 lines

  1. CONCEPT
  2.         mappings
  3.  
  4. LAST UPDATE
  5.         Mon, 15 Mar 1999
  6.  
  7. DESCRIPTION
  8.  
  9.   A step-by-step introduction to mappings:
  10.   ----------------------------------------
  11.  
  12.   1. What is a mapping?
  13.  
  14.     A mapping is a datatype which allows to store data associated to a key.
  15.   In other languages  they are also  known  as 'dictionaries' or  'alists'.
  16.   There are also alists in LPC but they are not a separate datatype but are
  17.   implemented on  top of arrays.  Alists are  the predecessors of mappings.
  18.   The keys and the values  can be of  any type.  But most common  datatypes
  19.   for keys are strings, integers and objects.  Others like arrays, mappings
  20.   or closures aren't a good choice because comparision between i.e.  arrays
  21.   often returns false  even if they equal  in content.  This is because the
  22.   driver compares i.e. two arrays by their internal pointers and not by
  23.   their content. The reason for this is simple: speed.
  24.  
  25.     Mappings  are allways  treated  as references    when passing  them  to
  26.   functions. This means when you pass a mapping  to another object and this
  27.   object modifies the mapping the modification will  take place in a global
  28.   scope - visible to all objects holding this mapping in a variable.
  29.  
  30.  
  31.   2. What are mappings good for?
  32.  
  33.     The term 'dictionary'  probably describes the  use  of a mapping  best.
  34.   Opposed  to arrays mappings don't have  a specific  order. They provide a
  35.   mechanism to   create  a set  of associations  between  values.  Such  an
  36.   association consists of a unique  key and data  that is identified by the
  37.   key. Think of  a dictionary  where you have  a  word and a definition  of
  38.   it. You use the word to lookup its definition.
  39.  
  40.     Mappings can be used i.e.  to hold  aliases for commands. The key would
  41.   then be the  name of  the alias and  the  data the command(s) behind   an
  42.   alias.  Or they can be used for  the exits of a  room.  The keys would be
  43.   the directions where one can go  to and the associated  data would be the
  44.   file names of the  rooms.  But mappings can  also be used  as a kind of a
  45.   sparse array.   A  sparse array is  an  array where most of  the elements
  46.   aren't used  (occupied by 0).  I.e.  if  you want to  store values at the
  47.   position 0, 13  and 37642 of an  array you would  have to create an array
  48.   with a size of at least 37643.  This  costs a lot  of memory so a mapping
  49.   would be  more useful because you would  then use the  numbers 0,  13 and
  50.   37642 as a key and not as an index to a position  (actually the keys of a
  51.   mapping are sometimes  called indices  but this  is just  because the way
  52.   data is accessed in a mapping is similar to  arrays: by the [] operator).
  53.   This also allows to  query all occupied   positions of a sparse array  by
  54.   querying for all  the keys of  the mapping opposed  to an array where you
  55.   have to iterate over all elements.
  56.  
  57.  
  58.   3. How do I create a mapping?
  59.  
  60.     There are several ways to do so. The most convenient is the following:
  61.  
  62.       mapping map;
  63.       map = ([ key0: value00; ...; value0n,
  64.                ... : ...    ; ...; ...    ,
  65.                keyn: valuen0; ...; valuenn ]);
  66.  
  67.     As you can see, a key may  have more than  one value assigned.  But the
  68.   amount of values per key must always be equal.  It  is  even  possible to
  69.   have mappings without any values!
  70.     Another  method is  to use the   efun mkmapping().  This  efun gets two
  71.   arguments with the first beeing an array of keys and the following beeing
  72.   arrays of values:
  73.  
  74.       mapping map;
  75.       map = mkmapping (({ key0   , ..., keyn    }),
  76.                        ({ value00, ..., value0n }),
  77.                        ({ ...    , ..., ...     }),
  78.                        ({ valuen0, ..., valuenn }));
  79.  
  80.     If the efun only gets one argument, then this argument will be taken as
  81.   an array of keys and a mapping  without values will  be returned.
  82.  
  83.     An empty mapping can be created by using the above described methods by
  84.   simply ommitting the keys and values:
  85.  
  86.       mapping map;
  87.       map = ([]);
  88.   or:
  89.       map = mkmapping(({}), ({}));
  90.  
  91.     Or  by  using the efun   allocate_mapping().  This efun gets  as  first
  92.   argument the  amount  of keys which will  be  added soon and  an optional
  93.   second argument specifying the width of the mapping:
  94.  
  95.       map = m_allocate(n, width);
  96.  
  97.     The value <n>  may be a bit  confusing  since mappings shrink and  grow
  98.   dynamically. This value just tells the driver how 'long' this  mapping is
  99.   going to be  so proper memory  allocations  will be  performed to  reduce
  100.   the overhead of memory reallocation.  I.e.  if you want to read in a file
  101.   and store the  read data in  a mapping  you probably  know  the amount of
  102.   keys.  So you allocate  a mapping with this  efun and tell the driver how
  103.   much memory should  be allocated  by specifing  a proper <n>  value.
  104.   Thus causing  a    speedup when adding  the  read   data to the   mapping
  105.   afterwards.    The <width> just specifies how   many  values per key this
  106.   mapping is   going to have. If  no  width is given, 1  will  be  taken as
  107.   default.
  108.  
  109.  
  110.   4. How can I modify the data of a mapping?
  111.  
  112.     Adding a  new key is similiar to   modifying the associated  data of an
  113.   existing key:
  114.  
  115.       map += ([ key: value0; ...; valuen ]);
  116.  
  117.     Or in case only a single value should be modified:
  118.  
  119.       map[key, n] = valuen;
  120.  
  121.     If  <n> is out of  range or if <key> doesn't  exists and <n> is greater
  122.   than 0 an "Illegal index" error will be reported. If <n> is equal to 0 or
  123.   the mapping only has a single value per key one can abbreviate it with:
  124.  
  125.       map[key] = value;
  126.  
  127.     If there is no <key> (and <n> is equal to 0 or  not specified at all) a
  128.   new one will be added automatically.
  129.  
  130.     Deletion   of a key    is  done with    the  -=  operator or  the  efun
  131.   m_delete(). A mapping can only be substracted by one without any values:
  132.  
  133.       map -= ([ key ]);
  134.   or:
  135.       map -= ([ key0, ..., keyn ]);
  136.  
  137.   The efun takes a mapping as first and a key as second argument:
  138.  
  139.       m_delete(map, key);
  140.  
  141.     The  efun   m_delete() returns  the mapping   but because  mappings are
  142.   handled as references there is no need of an assignment like:
  143.  
  144.       map = m_delete(map, key);
  145.  
  146.  
  147.   5. How can I access the data stored in a mapping?
  148.  
  149.     This can be done by:
  150.  
  151.       valuen = map[key, n];
  152.  
  153.     Or in case of a mapping with just one value per key:
  154.  
  155.       value0 = map[key];
  156.  
  157.     If there is no  <key> in the mapping  and <n> is  0 or not specified at
  158.   all (which is the same) a 0 will be returned or if <n>  is greater than 0
  159.   an "Illegal index" error will be reported.
  160.  
  161.  
  162.   6. How can I test for the existance of a key?
  163.  
  164.     A  return value of 0 is  sufficient for most applications but sometimes
  165.   the ambiguity  between an existing value of  0 and  a nonexisting key can
  166.   lead   to  a  problem.  Therefore   one  can use   the  efun member()  or
  167.   mapping_contains() to check if there actually is a key in the mapping:
  168.  
  169.       if (member(map, key)) {
  170.         ...
  171.       }
  172.   or:
  173.       if (mapping_contains(&value0, ..., &valuen, map, key)) {
  174.         ...
  175.       }
  176.  
  177.     This also shows how  one can retrieve all values   associated to a  key
  178.   from a mapping in a single step. The '&' is  the reference operator which
  179.   is neccesary to let the efun store the values in the variables.
  180.  
  181.     In case   of  mappings   with   no  values,   the  efun   member()  and
  182.   mapping_contains() are equal in their behaviour  and their way of calling
  183.   because mapping_contains() won't get any reference variables to store the
  184.   values in (obviously, because there aren't any).
  185.  
  186.      Also normally member() is known to return the postion of an element in
  187.   a list (i.e.  a  character in a  string or data   in an array) and if  an
  188.   element couldn't be  found -1 is returned.   But in the case  of mappings
  189.   there are no such things as order and postion. So member() only returns 0
  190.   or 1.
  191.  
  192.  
  193.   7. How can I copy a mapping?
  194.  
  195.     A  mapping can  be  copied   with  the  +  operator   or by the    efun
  196.   copy_mapping():
  197.  
  198.       newmap = ([]) + map;
  199.   or:
  200.       newmap = copy_mapping(map);
  201.  
  202.     A mapping should only be copied when it is neccesary to get an own copy
  203.   of it that  must not be  shared by other objects.
  204.  
  205.  
  206.   8. How can I get all keys of a mapping?
  207.  
  208.     The  efun m_indices() gets a mapping  as argument  and returns an array
  209.   holding all keys defined in this mapping:
  210.  
  211.       keys = m_indices(map);
  212.  
  213.  
  214.   9. How can I get all the values of a mapping?
  215.  
  216.     The efun m_values() gets  a mapping as  argument  and returns  an array
  217.   holding all the first (second, ...) values of it.
  218.  
  219.       values0 = m_values(map);     returns the first values
  220.       values0 = m_values(map, 0);  dito
  221.       values1 = m_values(map, 1);  returns the second values
  222.         etc
  223.  
  224.  
  225.   10. How can I determine the size of a mapping?
  226.  
  227.     Because a mapping is a kind of rectangle it has two sizes: a length and
  228.   a width.  There are three different efuns  to query these values. The first
  229.   two are the  efuns sizeof(), which returns the  amount of key-value
  230.   associations (the length of  a mapping), and widthof(), which returns the
  231.   number of values per key (the width). The third is the efun get_type_info().
  232.   get_type_info() is meant  to be a function  to identify a datatype.   Its
  233.   return value is an  array of two  numerical values.  The first  specifies
  234.   the datatype   of the argument and   the second is a   datatype dependend
  235.   value. In the case of a mapping the first value  is T_MAPPING (which is a
  236.   value defined in  <lpctypes.h>) and the  second the amount of values  per
  237.   key (a.k.a.  columns or the width  of the mapping  - actually it would be
  238.   correct to say that the width of a mapping is the  amount of columns plus
  239.   one for the keys but this is uncommon).
  240.  
  241.  
  242.   11. What is the best method to iterate over a mapping?
  243.  
  244.     First of all the main purpose of a mapping is not meant to  be a set of
  245.   data to iterate over. Afterall the keys in a mapping have no specific but
  246.   a random order (at least on the LPC side).  But  still it is possible and
  247.   sometimes even neccesary to do so.
  248.  
  249.     If all key-value associations  should be processed  then one should use
  250.   walk_mapping().  If all keys of a mapping should be processed to create a
  251.   new mapping being a subset of the given one, then filter_mapping() should
  252.   be  used.  If all  keys  are going to  be  processed and  to create a new
  253.   mapping with the  same set of keys as  the given mapping, then one  would
  254.   use map_mapping().  But in the case of an  iteration that should/can stop
  255.   even if not all data is processed it is probably wise to iterate over the
  256.   mapping by first querying for the keys and then to iterate over them with
  257.   a for() or a while() loop and querying the values by 'hand'.
  258.  
  259.     The efun walk_mapping() gets  a mapping as  first argument and the name
  260.   of a function  as second one. All the  following arguments are treated as
  261.   extras which  will  be  passed to the   function specified  with the  2nd
  262.   argument. Instead of a string for the name of a function a closure can be
  263.   used, too. Nothing will be returned:
  264.  
  265.       ...
  266.       walk_mapping(map, "func", xarg0, ..., xargn);
  267.       ...
  268.  
  269.       void func(mixed key, mixed value0, ..., mixed valuen,
  270.                 mixed xarg0, ..., mixed xargn) {
  271.         ...
  272.       }
  273.  
  274.     func() will be called for all key-value associations  and gets as first
  275.   argument the key.  The next arguments are the  values behind the key  and
  276.   are passed as references.  The  rest  of the  passed arguments are  those
  277.   specified as extras. Because the values are passed as references (opposed
  278.   to  copies) it is possible  to modify them  from  inside func() by simply
  279.   assigning new value to the variables <value0>, ..., <valuen>.
  280.  
  281.     The efun filter_mapping() calls  a function for  each key in  a mapping
  282.   and creates a new mapping  which only contains key-value associations for
  283.   which the called function returned true (not  equal 0 that is). The first
  284.   argument is the mapping to iterate over and the second is a function name
  285.   given as a string or a closure:
  286.  
  287.       ...
  288.       submap = filter_mapping(map, "func", xarg0, ..., xargn);
  289.       ...
  290.  
  291.       int func(mixed key, mixed xarg0, ..., mixed xargn) {
  292.         ...
  293.       }
  294.  
  295.     func() gets  as first argument the key  and the others are those passed
  296.   as extras to filter_mapping().
  297.  
  298.     The efun map_mapping() gets a mapping as first argument and a string as
  299.   a function name (or again a closure) as  second argument.  Any additional
  300.   arguments are again used  as extras that will  be passed to the iteration
  301.   function. This efun returns a new mapping with the same keys as the given
  302.   one.  The values  returned by the function  that is invoked  for each key
  303.   will be used as the associated data behind each key of the new mapping:
  304.  
  305.       ...
  306.       newmap = map_mapping(map, "func", xarg0, ..., xargn);
  307.       ...
  308.  
  309.       mixed func(mixed key, mixed xarg0, ..., mixed xargn) {
  310.         ...
  311.       }
  312.  
  313.     func() gets  as first argument the key  and the others are those passed
  314.   as extras to map_mapping().
  315.  
  316.     Because a function can only return  a single value  (even when it is an
  317.   array) it restricts the use  of map_mapping() to  only allow creation  of
  318.   mappings with a single value per key.
  319.  
  320.  
  321.   12. Is it possible to join/intersect/cut mappings with another?
  322.  
  323.     Joining mappings is only possible, if  they have the same width (amount
  324.   of values per key). One can use the + and += operator:
  325.  
  326.       map = map1 + map2 + ... + mapn;
  327.       map += map1 + map2 + ... + mapn;
  328.  
  329.     Intersection     of   two   mappings is    only      possible by  using
  330.   filter_mapping(). There is  no efun or operator  which features this. The
  331.   'easiest' way may be the following function:
  332.  
  333.       mapping intersect_mapping(mapping map1, mapping map2) {
  334.         closure cl;
  335.  
  336.         cl = lambda(({ 'key }), ({ #'member, map2, 'key }));
  337.         return filter_mapping(map1, cl, map2);
  338.       }
  339.  
  340.     This function returns a  new mapping which   consists of all  key-value
  341.   associations   of  <map1>  for which  an  equal  key  could   be found in
  342.   <map2>. This function uses  a closure which returns 0  or 1  depending on
  343.   wether a key from <map1> is contained in <map2> or not.
  344.  
  345.     Cutting out  all key-value associations of a   mapping for which  a key
  346.   could be  found in another mapping  can  be done  by using  the  - and -=
  347.   operator:
  348.  
  349.       mapping cut_mapping(mapping map1, mapping map2) {
  350.         return map1 - mkmapping(m_indices(map2));
  351.       }
  352.  
  353.     Because a maping can  only be substracted by one  without any values we
  354.   first have to create such by using m_indices() and mkmapping().
  355.  
  356.  
  357.   13. What are those mappings without any values (besides keys) good for?
  358.  
  359.     Because the way how the driver  searches for a  key in a mapping is
  360.   rather fast, those mappings can be used as a  set of elements with a fast
  361.   method for testing if an element is  contained in the set. This technique
  362.   is called hashing (further  explanation   would lead  too far)  which  is
  363.   faster  than searching for  values  in array  (which  is done in a linear
  364.   fashion).
  365.  
  366.     Another (maybe  more pratical) use  of these  mappings  are to create a
  367.   array of unique values out of an array with several equal values:
  368.  
  369.       uniques = m_indices(mkmapping(array));
  370.  
  371.     mkmapping() uses  <array> to  create  a mapping without any  values but
  372.   just keys. And  because a mapping can only  have unique keys all multiple
  373.   values in <array> are taken as one.  The call of m_indices() then returns
  374.   an  array  of  these  unique keys.  Actually we  only  make  use of those
  375.   mappings temporarily.
  376.  
  377.  
  378.   14. How can I convert an alist into a  mapping and vice versa?
  379.  
  380.     There are no special efuns which handle such conversions. But it can be
  381.   done by the following functions:
  382.  
  383.       mapping alist_to_mapping(mixed *alist) {
  384.         return apply(#'mkmapping, alist);
  385.       }
  386.  
  387.     The efun apply() takes a closure and an array of values and passes each
  388.   element of the  array as an  argument  to the  closure. Because  an alist
  389.   consists of an array of arrays with the first beeing the list of keys and
  390.   the others the values associated to each key passing them as arguments to
  391.   the efun closure #'mkmapping via apply() causes the creation of a mapping
  392.   out of an alist.
  393.  
  394.       mixed *mapping_to_alist(mapping map) {
  395.         mixed *alist;
  396.         symbol *vars;
  397.         string var;
  398.         closure cl;
  399.         int width;
  400.  
  401.         width = get_type_info(map)[1];
  402.         alist = allocate(width + 1);
  403.         vars  = allocate(width + 2);
  404.         for (var = "a"; width; var[0]++, width--) {
  405.           alist[width] = ({});
  406.           vars[width]  = quote(var);
  407.         }
  408.         alist[0] = ({});
  409.         vars[0]  = 'key;
  410.         vars[<1] = 'alist;
  411.         cl = lambda(vars, ({ #'=, 'alist, ({ #'insert_alist }) + vars }));
  412.         walk_mapping(map, cl, &alist);
  413.         return alist;
  414.       }
  415.  
  416.     This function is  a bit more  complicated  than the other  and detailed
  417.   description would lead   too far of   the topic.  This  function has  one
  418.   restriction:  it can only turn a  mappings with up to  26  values per key
  419.   into an  alist.    But  this  should  be   sufficient for probably    all
  420.   applications which use mappings.
  421.  
  422.   And Hyps further comment on this:
  423.         The function mapping_to_alist() is also not that
  424.         clever because insert_alist() allways creates a new
  425.         alist.  A second (optional) argument to m_values() to
  426.         specify the value column would be better. Besides
  427.         this, the conversion of a mapping into an alist could
  428.         be done by to_array().
  429.  
  430. SEE ALSO
  431.         alists(LPC), closures(LPC), mkmapping(E), walk_mapping(E)
  432.